package jmine.tec.test.testcase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.apache.log4j.Logger;
import org.springframework.transaction.TransactionStatus;

import bancosys.tec.persist.authorization.AuthorizationContext;
import bancosys.tec.persist.authorization.Authorizer;
import bancosys.tec.persist.bussobj.AuthPersistableBusinessObject;
import bancosys.tec.persist.persister.DefaultPersister;
import bancosys.tec.persist.persister.Persister;
import bancosys.tec.persist.persister.PurePersister;

/**
 * @created Jun 14, 2006
 * @author Gustavo Almeida
 * @param <T> tipo do bean testado.
 */
public abstract class AuthBOBaseTestCase<T extends AuthPersistableBusinessObject> extends AbstractBOBaseTestCase<T> {
    /** log4j */
    private static final Logger LOG = Logger.getLogger(AuthBOBaseTestCase.class);

    /**
     * Contrutor.
     * 
     * @throws PersistenceException
     */
    public AuthBOBaseTestCase() {
        super();
    }

    /**
     * Construtor
     * 
     * @param name do test case.
     * @throws PersistenceException
     */
    public AuthBOBaseTestCase(String name) {
        super(name);
    }

    /**
     * testa a operação save seguida do reject.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testSaveAndReject() throws Exception {
        LOG.trace("testSaveAndReject()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        try {
            collection = this.findAllData();
            assertEquals("A base deveria estar inicialmente sem objetos do tipo " + this.getNewBOInstance().getClass().getName()
                    + " na partição quente.", 0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals("A base deveria estar inicialmente sem objetos do tipo " + this.getNewBOInstance().getClass().getName()
                    + " na partição fria.", 0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        status = this.getHelper().getTransactionStatus();
        T bo;
        try {
            bo = this.getNewBOInstance();
            this.fillData(0, bo);
            this.preencheIdNosTestes(bo);
            this.saveBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals("Não deveria estar na quente", 0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        // deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals("Deveria estar na fria", 1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);

            // rejeita
            this.rejectBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * testa a operação save seguida do authorize.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testSaveAndAuthorize() throws Exception {
        LOG.trace("testSaveAndAuthorize()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        T bo;
        try {
            this.findAllData();
            bo = this.getNewBOInstance();
            this.fillData(0, bo);
            this.preencheIdNosTestes(bo);
            this.saveBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na quente
        status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        // deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);
            this.comparaIdNosTestes(bo);

            // autoriza
            this.authorizeBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * testa a operação update seguida do reject.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testUpdateAndReject() throws Exception {
        LOG.trace("testUpdateAndReject()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        T bo;
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.fillData(1, bo);
            this.preencheIdNosTestes(bo);
            this.saveBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a velha deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);

            // rejeita
            this.rejectBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a velha deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);
            this.comparaIdNosTestes(bo);

        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * testa a operação save update do authorize.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testUpdateAndAuthorize() throws Exception {
        LOG.trace("testUpdateAndAuthorize()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        T bo;
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.fillData(1, bo);
            this.preencheIdNosTestes(bo);
            this.saveBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a velha deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(0, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);

            // autoriza
            this.authorizeBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * testa a operação remove seguida do reject.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testRemoveAndReject() throws Exception {
        LOG.trace("testRemoveAndReject()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        T bo;
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.removeBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a velha deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());

            // rejeita
            bo = collection.iterator().next();
            this.rejectBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * testa a operação remove seguida do authorize.
     * 
     * @throws Exception joga pra cima toda exceção lançada.
     */
    public void testRemoveAndAuthorize() throws Exception {
        LOG.trace("testRemoveAndAuthorize()");

        TransactionStatus status = this.getHelper().getTransactionStatus();
        Collection<T> collection;
        T bo;
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.removeBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a velha deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());
            bo = collection.iterator().next();
            this.compareData(1, bo);
            this.comparaIdNosTestes(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // a nova deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(1, collection.size());

            // autoriza
            bo = collection.iterator().next();
            this.authorizeBO(bo);
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na quente
        status = this.getHelper().getTransactionStatus();
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }

        // nao deve estar na fria.
        status = this.getHelper().getTransactionStatus(AuthorizationContext.cold);
        try {
            collection = this.findAllData();
            assertEquals(0, collection.size());
        } finally {
            this.getHelper().commit(status);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doSaveTestData(T bo) {
        Persister<T> adapter = new PurePersister<T>();
        this.configurePersister(bo, adapter);
        adapter.save();
    }

    /**
     * Devolve um persister configurado para esse bean.
     * 
     * @param bo bean
     * @return persister.
     */
    protected DefaultPersister<T> getPersister(T bo) {
        Persister<T> adapter = new DefaultPersister<T>();
        this.configurePersister(bo, adapter);
        return (DefaultPersister<T>) adapter;
    }

    /**
     * Configura o persister para o teste.
     * 
     * @param bo bean.
     * @param persister persister.
     */
    private void configurePersister(T bo, Persister<T> persister) {
        persister.setValidatorFactory(this.getValidatorFactory());
        persister.setSessionFactory(this.getHelper().getSessionFactory());
        persister.setAuditManager(this.getHelper().getAuditManager());
        persister.setTarget(bo);
    }

    /**
     * Salva o bean.
     * 
     * @param bo bean.
     */
    protected void saveBO(T bo) {
        Persister<T> adapter = this.getPersister(bo);
        adapter.save();
    }

    /**
     * remove o bean.
     * 
     * @param bo bean.
     */
    protected void removeBO(T bo) {
        Persister<T> adapter = this.getPersister(bo);
        adapter.remove();
    }

    /**
     * authoriza o bean.
     * 
     * @param bo bean.
     */
    protected void authorizeBO(T bo) {
        Authorizer<T> adapter = this.getPersister(bo);
        adapter.authorize();
    }

    /**
     * rejeita o bean.
     * 
     * @param bo bean.
     */
    protected void rejectBO(T bo) {
        Authorizer<T> adapter = this.getPersister(bo);
        adapter.reject();
    }

    /**
     * Devolve a suite padrão.
     * 
     * @param clazz classe do testcase
     * @param tests additional tests
     * @return suite de testes
     */
    public static TestSuite getDefaultSuite(Class<? extends TestCase> clazz, String... tests) {
        List<String> defaultList =
                new ArrayList<String>(Arrays.asList("testSaveAndReject", "testSaveAndAuthorize", "testUpdateAndReject",
                        "testUpdateAndAuthorize", "testRemoveAndReject", "testRemoveAndAuthorize", "testDAOQuerySyntax", "testGetTestData"));
        defaultList.addAll(Arrays.asList(tests));
        return getSuite(clazz, defaultList.toArray(new String[defaultList.size()]));
    }
}
